
import hashlib
import json
from time import time
from urllib.parse import urlparse
import random
import string


class Blockchain:
    def __init__(self):
        '''
            初始化区块链；定义链的几个属性：current_transaction存当前交易的哈希；chain存区块链；nodes存节点信息，然后生成创世区块
        '''
        self.current_transactions = ""
        self.prev_hash = ""
        self.chain = []
        self.nodes = set()

    def genesis_block(self):
        block = {
            'index': len(self.chain) + 1,
            'transactions': "Welcome to Distributed blockchain model -SCU-Zhou Zunlong",
            'previous_hash': "0",
            'proof': 10862,
        }
        self.chain.append(block)
        self.prev_hash = self.hash(block)
        # print(self.chain)

    def register_node(self, address):
        """
        添加注册一个区块，比如 http://192.167.0.124:5000
        """
        parsed_url = urlparse(address)
        if parsed_url.netloc:
            self.nodes.add(parsed_url.netloc)
        elif parsed_url.path:
            # 允许不带协议头： '192.168.0.5:5000'.
            self.nodes.add(parsed_url.path)
        else:
            raise ValueError('Invalid URL')

    def valid_chain(self, chain):
        """
        验证给出的区块链是否合法
        """
        last_block = chain[0]
        current_index = 1

        while current_index < len(chain):
            block = chain[current_index]
            print(f'{last_block}')
            print(f'{block}')
            print("\n-----------\n")
            # 检查当前前置哈希是否是前一个区块的哈希
            if block['previous_hash'] != self.hash(last_block):
                return False

            # Check that the Proof of Work is correct
            if not self.valid_proof(last_block['proof'], block['proof'], last_block['previous_hash']):
                return False

            last_block = block
            current_index += 1

        return True

    def resolve_conflicts(self):
        """
        合并链上的冲突，取最长链
        """

        neighbours = self.nodes
        new_chain = None

        # We're only looking for chains longer than ours
        max_length = len(self.chain)

        # Grab and verify the chains from all the nodes in our network
        for node in neighbours:
            response = requests.get(f'http://{node}/chain')

            if response.status_code == 200:
                length = response.json()['length']
                chain = response.json()['chain']

                # Check if the length is longer and the chain is valid
                if length > max_length and self.valid_chain(chain):
                    max_length = length
                    new_chain = chain

        # Replace our chain if we discovered a new, valid chain longer than ours
        if new_chain:
            self.chain = new_chain
            return True

        return False

    def new_block(self, proof,  transactions):
        """
        Create a new Block in the Blockchain
        :param proof: The proof given by the Proof of Work algorithm
        :param previous_hash: Hash of previous Block
        :return: New Block
        """

        block = {
            'index': len(self.chain) + 1,
            'transactions': transactions or self.current_transactions,
            'previous_hash': self.prev_hash or self.hash(self.chain[-1]),
            'proof': proof,
        }

        # Reset the current list of transactions
        self.current_transactions = []

        self.chain.append(block)
        self.prev_hash = self.hash(block)
        return block

    def new_trans(self):
        self.current_transactions = ''.join(random.choices(string.ascii_letters + string.digits, k=256))
        return self.current_transactions

    @property
    def last_block(self):
        return self.chain[-1]

    @property
    def get_chain(self):
        return self.chain

    @property
    def get_chainlen(self):
        return len(self.chain)

    @staticmethod
    def hash(block):
        """
        获取一个区块的哈希值
        """
        # print(block)
        block_string = str(block['index']) + block['transactions'] + block['previous_hash'] + str(block['proof'])
        sha256 = hashlib.sha256()
        sha256.update(block_string.encode('utf-8'))
        return sha256.hexdigest()


    @staticmethod
    def valid_proof(self, block):
        return self.hash(block) == "0000"